home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / September 93.iso / Archives / Sound / Playing & Recording / Macintosh Tracker / Tracker Server Folder / mac_audio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-28  |  35.4 KB  |  1,093 lines  |  [TEXT/KAHL]

  1. /* macintosh_audio.c */
  2.  
  3. /* All the stuff in this file was written by Thomas R. Lawrence. */
  4. /* See the "mac_readme" or "mac_programmer_info" files for more information */
  5. /* about the Macintosh port */
  6.  
  7. #include <Sound.h>
  8. #include <sane.h>
  9. #include "mac_event.h"
  10.  
  11. #include <math.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14.  
  15. #include "defs.h"
  16. #include "extern.h"
  17. #include "song.h"
  18. #include "channel.h"
  19.  
  20. #define CONSTRAIN(value,min,max) MAX(MIN(value,max),min)
  21.  
  22. #define MAXNUMBUFFERS (128)
  23. #define MAXBUFFERSIZE (8192)
  24. #define MinFreeMem (32768 + MAXBUFFERSIZE + 256)
  25.  
  26. /* comment this out to use C-code */
  27. #define USE_ASSEMBLY_CODE
  28.  
  29. typedef struct MyS
  30.   {
  31.     struct MyS*     Next; /* circular linked list embedded in a static array */
  32.     SndCommand      MySoundCommand;
  33.     SndCommand      MyCallbackCommand;
  34.     volatile short  InUseFlag; /* interrupt level flag; hence "volatile" */
  35.     ExtSoundHeader  Header;
  36.     char            SampleArea[MAXBUFFERSIZE];
  37.   } MyStructure;
  38.  
  39. static pascal void    MyCallBack(SndChannel* Channel, SndCommand* Command);
  40.  
  41. extern Boolean                QuitPending;
  42.  
  43. static int            mac_stereo;
  44. /* 256th of primary/secondary source for that side. */
  45. static int            primary=256, secondary=0;
  46. extern short          Loudness;
  47.  
  48. /* the following array is grouped as 16*256 2-byte words.  Bits 9..12 of the */
  49. /* index are the most significant bits of the fraction of the pointer for */
  50. /* anti-aliasing.  Bits 1..8 are the sample's value.  If Bit 0 of the address is */
  51. /* 0, then this is the partially weighted left hand side for the antialiasing */
  52. /* average.  Bit 0 == 1 is the right hand side.  The left hand side is */
  53. /* the fraction * sample.  The right hand side is 2s complement of fraction * sample */
  54. static char*          AliasTable = NULL;
  55.  
  56. /* the following array is a 1024-byte array for converting samples overall loudness */
  57. /* with clipping instead of roll-over.  This is because each raw sample value */
  58. /* ranges from -128..127.  We convert this to unsigned for the table: 0..255 */
  59. /* There are 4 channels, so the maximum is 4*255 == 1020.  Thus, we provide */
  60. /* 1024 (a nice round number) values.  When the table is constructed, the */
  61. /* values are constrained (i.e. clipped) so that rollover doesn't occur.  This */
  62. /* makes the sound a bit better when the volume is high enough to cause */
  63. /* rollover.  The maximum value of abs(FinalVolumeTablePrimary) is "primary" */
  64. /* and the max of abs(FinalVolumeTableSecondary) is "secondary".  See */
  65. /* "resample" for usage notes */
  66. static char*          FinalVolumeTablePrimary = NULL;
  67. static char*          FinalVolumeTableSecondary = NULL;
  68.  
  69. /* the following array is a 0..64*256 entry array for converting volume */
  70. /* (in the upper bits) and an 8-bit sample value into a new sample value. */
  71. /* an upper value of 64 represents no scaling. */
  72. /* You use this like this: */
  73. /* SubVolumeTable[<8-bit sample> + 256 * <6-bit volume level>] */
  74. static short*         SubVolumeTable = NULL;
  75.  
  76. /* the following array is used for workspace */
  77. static long           WorkspaceBytes = 0;
  78. static short*         WorkspaceArray1 = NULL;
  79. static short*         WorkspaceArray2 = NULL;
  80.  
  81. static SndChannel*    MySoundChannel; /* here's where it all happens, folks */
  82.  
  83. static MyStructure*   Buffers[MAXNUMBUFFERS];
  84. static int                        ActualNumBuffers;
  85. static MyStructure*   CurrentBuffer;
  86. static int            buf_index;
  87.  
  88. short                                    Pausing = false;
  89.  
  90.  
  91. /* this is called from the event cycle which is called from the synthesizer via */
  92. /* the invokation of "may_getchar()".  When the program is playing */
  93. /* (Pausing == false), it sets Pausing to true, pauses the Macintosh sound */
  94. /* channel, and then spinwaits, calling may_getchar to provide processing time */
  95. /* to other programs.  When the event to unpause is received, this program is */
  96. /* invoked again (the first activation record is still present--recursive */
  97. /* invokation).  The second call unpauses the sound channel and clears the flag. */
  98. /* As the call stack is unrolled, control will be returned here.  Since the */
  99. /* Pausing flag is now clear, this routine will exit and things will carry */
  100. /* on normaly.  FLAW:  pauseCmd does not seem to have an effect.  The buffered */
  101. /* sound will play out and sound will stop only when all prepared buffers */
  102. /* have been played.  I don't know how to fix this; it seems to be a problem in */
  103. /* the Macintosh OS, not in this program. */
  104. void                TogglePause(void)
  105.     {
  106.         SndCommand            MyCommand;
  107.  
  108.         if (Pausing)
  109.             {
  110.                 Pausing = false;
  111.                 MyCommand.cmd = resumeCmd;
  112.                 MyCommand.param1 = 0;
  113.                 MyCommand.param2 = 0;
  114.                 SndDoImmediate(MySoundChannel,&MyCommand);
  115.             }
  116.          else
  117.             {
  118.                 Pausing = true;
  119.                 MyCommand.cmd = pauseCmd;
  120.                 MyCommand.param1 = 0;
  121.                 MyCommand.param2 = 0;
  122.                 SndDoImmediate(MySoundChannel,&MyCommand);
  123.                 while (Pausing && !QuitPending)
  124.                     {
  125.                         may_getchar();
  126.                     }
  127.             }
  128.     }
  129.  
  130.  
  131. /* adjust the stereo mixing.  Percent is 0..100; each channel takes on a value */
  132. /* in 0..256, providing 8-bits of scaling.  At full volume for a channel, */
  133. /* 256 represents a shift left by 8.  Thus, a shift right by 8 is the necessary */
  134. /* correction after scaling.  "primary" is for the main channel and "secondary" */
  135. /* is for the other channel.  They do not correspond strictly to left and right. */
  136. void      set_mix(int percent)
  137.   {
  138.     percent *= (256/2);
  139.     percent /= 100;
  140.     secondary = percent;
  141.     primary = 256 - percent;
  142.   }
  143.  
  144.  
  145. /* recalibrate the volume lookup table using a new value.  This is done on */
  146. /* the fly as volume levels are adjusted. */
  147. void      ResetVolumeTable(void)
  148.   {
  149.     short        Scan;
  150.     short        Intermediate;
  151.     short        Radius;
  152.  
  153.     if (FinalVolumeTablePrimary == NULL)
  154.       {
  155.         FinalVolumeTablePrimary = (char*)NewPtr(1024 * sizeof(char));
  156.         if (FinalVolumeTablePrimary == NULL)
  157.           {
  158.             perror("Not enough memory to play song.");
  159.             end_all();
  160.           }
  161.       }
  162.     /* primary ranges between 0..255; abs(sample) ranges between 0..127. */
  163.     /* so we divide by 2 and subtract 1 just in case -128 gets in.  The radius */
  164.     /* is the maximum value we'll allow the sample to be.  Anything above */
  165.     /* is clipped.  Primary Radius + Secondary Radius == 127 (barring rounding */
  166.     /* errors.)  Thus we clip just before arithmetic rollover occurs. */
  167.     Radius = (primary / 2) - 1;
  168.     if (Radius < 0)
  169.       {
  170.         Radius = 0;
  171.       }
  172.     for (Scan = -512; Scan <= 511; Scan += 1)
  173.       {
  174.         Intermediate = (Scan * Loudness * primary) / (64 * 256);
  175.         if (Intermediate > Radius)
  176.           {
  177.             Intermediate = Radius;
  178.           }
  179.         if (Intermediate < -Radius)
  180.           {
  181.             Intermediate = -Radius;
  182.           }
  183.         FinalVolumeTablePrimary[Scan & 0x03ff] = Intermediate;
  184.       }
  185.  
  186.     if (FinalVolumeTableSecondary == NULL)
  187.       {
  188.         FinalVolumeTableSecondary = (char*)NewPtr(1024 * sizeof(char));
  189.         if (FinalVolumeTableSecondary == NULL)
  190.           {
  191.             perror("Not enough memory to play song.");
  192.             end_all();
  193.           }
  194.       }
  195.     Radius = (secondary / 2) - 1;
  196.     if (Radius < 0)
  197.       {
  198.         Radius = 0;
  199.       }
  200.     for (Scan = -512; Scan <= 511; Scan += 1)
  201.       {
  202.         Intermediate = (Scan * Loudness * secondary) / (64 * 256);
  203.         if (Intermediate > Radius)
  204.           {
  205.             Intermediate = Radius;
  206.           }
  207.         if (Intermediate < -Radius)
  208.           {
  209.             Intermediate = -Radius;
  210.           }
  211.         FinalVolumeTableSecondary[Scan & 0x03ff] = Intermediate;
  212.       }
  213.   }
  214.  
  215.  
  216. int        open_audio(int SampleRate, int StereoFlag)
  217.   {
  218.     OSErr              Error;
  219.     short              Scan;
  220.     short              Index;
  221.     SndCommand         Cmd;
  222.     long double        Fred;
  223.     unsigned long      FixedSampleRate;
  224.     MyStructure*       LastBuffer;
  225.  
  226.     FixedSampleRate = ((long)SampleRate) << 16;
  227.     if (FixedSampleRate == 0)
  228.       {
  229.         FixedSampleRate = rate22khz;
  230.       }
  231.  
  232.     if (AliasTable == NULL)
  233.       {
  234.         AliasTable = (char*)NewPtr(16*256*2);
  235.         if (AliasTable == NULL)
  236.           {
  237.             perror("Not enough memory to play song.");
  238.             end_all();
  239.           }
  240.         for (Scan = 0; Scan <= 15; Scan += 1)
  241.           {
  242.             for (Index = -128; Index <= 127; Index += 1)
  243.               {
  244.                 short        TableIndex;
  245.  
  246.                                 /* we precompute the antialiasing multiplication table here. */
  247.                                 /* Scan == the high 4 bits of the fraction of the pointer which */
  248.                                 /* is accessing the sampled sound.  Thus 0 IS a sample point, */
  249.                                 /* 1 is just to the right of a sample point, and 15 is just to */
  250.                                 /* the left of a sample point.  As we approach the right, the */
  251.                                 /* value increases, so the right weight is just Scan.  As we */
  252.                                 /* approach the left, the weight increases, but Scan decreases */
  253.                                 /* so the value is 16-Scan.  When Scan == 0, the weight of the */
  254.                                 /* right is 0, and the full left value is used.  This is because */
  255.                                 /* at this point, the left sample is being pointed directly to */
  256.                                 /* so it's actual value should be returned. */
  257.                 TableIndex = ((Scan << 8) | (Index & 0x00ff)) << 1;
  258.                 AliasTable[TableIndex + 0] = ((16 - Scan) * Index) / 16;
  259.                 AliasTable[TableIndex + 1] = (Scan * Index) / 16;
  260.               }
  261.           }
  262.       }
  263.  
  264.     ResetVolumeTable();
  265.  
  266.     if (SubVolumeTable == NULL)
  267.       {
  268.         SubVolumeTable = (short*)NewPtr((MAX_VOLUME-MIN_VOLUME+1)*256*sizeof(short));
  269.         if (SubVolumeTable == NULL)
  270.           {
  271.             perror("Not enough memory to play song.");
  272.             end_all();
  273.           }
  274.         for (Scan = 0; Scan <= MAX_VOLUME - MIN_VOLUME; Scan += 1)
  275.           {
  276.             for (Index = -128; Index <= 127; Index += 1)
  277.               {
  278.                 SubVolumeTable[(Scan << 8) | (Index & 0x00ff)]
  279.                   = ((Scan + MIN_VOLUME) * Index + ((MAX_VOLUME - MIN_VOLUME)/2))
  280.                   / (MAX_VOLUME - MIN_VOLUME);
  281.                 /* normally, that would be MAX_VOLUME - MIN_VOLUME + 1, but */
  282.                 /* we want to be 0..1 instead of 0..0.99999 because MAX_VOLUME */
  283.                 /* should not scale the volume at all. */
  284.               }
  285.           }
  286.       }
  287.  
  288.     mac_stereo = StereoFlag;
  289.  
  290.     Error = SndNewChannel(&MySoundChannel,
  291.       sampledSynth /* what synthesizer to use */,
  292.       (StereoFlag * initStereo)
  293.         | initNoInterp /* we do oversampling ourselves */
  294.         | initNoDrop /* what the heck is drop-sample conversion? */,
  295.       (void*)MyCallBack /* == our buffer unmarking callback routine */);
  296.     if (Error != noErr)
  297.       {
  298.         perror("unable to open sound channel");
  299.         end_all();
  300.       }
  301.     Cmd.cmd = ampCmd;
  302.     Cmd.param1 = 255;
  303.     Cmd.param2 = 0;
  304.     Error = SndDoImmediate(MySoundChannel,&Cmd);
  305.     if (Error != noErr)
  306.       {
  307.         perror("unable to initialize sound channel");
  308.       }
  309.  
  310.         /* create an initial workspace */
  311.         WorkspaceBytes = (1500 * sizeof(short) + 3) & ~3L; /* should be plenty */
  312.     WorkspaceArray1 = (void*)NewPtr(WorkspaceBytes);
  313.     WorkspaceArray2 = (void*)NewPtr(WorkspaceBytes);
  314.     if ((WorkspaceArray1 == NULL) || (WorkspaceArray2 == NULL))
  315.       {
  316.                 FatalError(FatalErrorOutOfMemory);
  317.         perror("No memory to allocate workspace.");
  318.         end_all();
  319.       }
  320.  
  321.         /* we now try to make as many buffers as we have memory for, so we can */
  322.         /* precompute the composite samples to be played as far ahead as we can */
  323.         /* so that momentary losses of control don't make the sound skip.  We */
  324.         /* need at least 3 buffers, for double buffering and to make our full */
  325.         /* usage check work.  (We check to see if all buffers are waiting to be played */
  326.         /* and not empty by counting the number of ones with a marked "InUseFlag" */
  327.         /* However, one buffer (the one under construction) will always be NOT */
  328.         /* marked.  If we settle for 2 buffers, then when we account for the buffer */
  329.         /* we are constructing, it will always look like all buffers are in use */
  330.         /* until all have stopped.  The sound will skip, so we might as well not */
  331.         /* play it at all.  Hence the limit of 3 buffers.) */
  332.         Scan = 0;
  333.     do
  334.       {
  335.         MyStructure*      Temp;
  336.         long                            FreeMemory;
  337.  
  338.                 FreeMemory = FreeMem();
  339.                 if (FreeMemory < MinFreeMem)
  340.                     {
  341.                 if (Scan < 3)
  342.                   {
  343.                     perror("Not enough memory to play song!");
  344.                                 FatalError(FatalErrorOutOfMemory);
  345.                                 FatalError(FatalErrorOutOfMemory);
  346.                     end_all();
  347.                   }
  348.                  else
  349.                   {
  350.                       goto StopMakingBuffers;
  351.                   }
  352.                     }
  353.         Buffers[Scan] = (void*)NewPtr(sizeof(MyStructure));
  354.         Buffers[Scan]->InUseFlag = 0;
  355.         Buffers[Scan]->Header.samplePtr = &(Buffers[Scan]->SampleArea[0]);
  356.         if (mac_stereo)
  357.           {
  358.             Buffers[Scan]->Header.sampleSize = 8*2*sizeof(char);
  359.             Buffers[Scan]->Header.numChannels = 2;
  360.           }
  361.          else
  362.           {
  363.             Buffers[Scan]->Header.sampleSize = 8*sizeof(char);
  364.             Buffers[Scan]->Header.numChannels = 1;
  365.           }
  366.         Buffers[Scan]->Header.sampleRate = FixedSampleRate;
  367.         Buffers[Scan]->Header.loopStart = 0;
  368.         Buffers[Scan]->Header.loopEnd = 0;
  369.         Buffers[Scan]->Header.encode = extSH;
  370.         Buffers[Scan]->Header.baseFrequency = 64;
  371.         Buffers[Scan]->Header.markerChunk = NULL;
  372.         Buffers[Scan]->Header.futureUse1 = 0;
  373.         Buffers[Scan]->Header.futureUse2 = 0;
  374.         Buffers[Scan]->Header.futureUse3 = 0;
  375.         Buffers[Scan]->Header.futureUse4 = 0;
  376.         Fred = SampleRate;
  377.         x96tox80(&Fred,&(Buffers[Scan]->Header.AIFFSampleRate));
  378.         Scan += 1;
  379.       } while (Scan < MAXNUMBUFFERS);
  380.    StopMakingBuffers:
  381.     ActualNumBuffers = Scan;
  382.     for (Scan = 0; Scan < ActualNumBuffers; Scan += 1)
  383.       {
  384.           /* now we establish the circular linked list so that we can */
  385.           /* easily find out what the "next" buffer to use is */
  386.         Buffers[Scan]->Next = Buffers[(Scan + 1) % ActualNumBuffers];
  387.       }
  388.     CurrentBuffer = Buffers[0];
  389.     buf_index = 0;
  390.  
  391.     return FixedSampleRate >> 16;
  392.   }
  393.  
  394.  
  395. /* this evaluates to see if all but the current buffer have been filled */
  396. /* and are waiting to be played.  If they have, then we can take a break in */
  397. /* the event loop since there won't be any work to do for a while. */
  398. BOOL      is_channel_full(void)
  399.   {
  400.     int          Scan;
  401.     int                   Count;
  402.  
  403.         Count = 0;
  404.     for (Scan = 0; Scan < ActualNumBuffers; Scan += 1)
  405.       {
  406.         if (Buffers[Scan]->InUseFlag)
  407.           {
  408.               Count += 1;
  409.           }
  410.       }
  411.     if (Count >= ActualNumBuffers - 1)
  412.         {
  413.             return TRUE;
  414.         }
  415.      else
  416.            {
  417.                return FALSE;
  418.            }
  419.   }
  420.  
  421.  
  422. /* here we check to see if all the buffers have played out.  This is done */
  423. /* at the end so that we don't close the channel too early and cut off the */
  424. /* end of the song. */
  425. BOOL            is_channel_empty(void)
  426.     {
  427.         int                    Scan;
  428.  
  429.         for (Scan = 0; Scan < ActualNumBuffers; Scan += 1)
  430.             {
  431.                 if (Buffers[Scan]->InUseFlag)
  432.                     {
  433.                         return FALSE; /* nope, buffers are still in use */
  434.                     }
  435.             }
  436.         return TRUE;
  437.     }
  438.  
  439.  
  440. /* this counts the number of buffers waiting to be played.  The event loop uses */
  441. /* this to get an idea of how long it can sit around before worrying about */
  442. /* filling some more buffers. */
  443. short                NumberPendingBlocks(void)
  444.   {
  445.     int          Scan;
  446.     int                   Count;
  447.  
  448.         Count = 0;
  449.     for (Scan = 0; Scan < ActualNumBuffers; Scan += 1)
  450.       {
  451.         if (Buffers[Scan]->InUseFlag)
  452.           {
  453.               Count += 1;
  454.           }
  455.       }
  456.     return Count;
  457.   }
  458.  
  459.  
  460. /* queues the data to be played by the sound manager. */
  461. void      actually_flush_buffer(void)
  462.   {
  463.     OSErr                    Error;
  464.  
  465.     while (CurrentBuffer->Next->InUseFlag)
  466.       {
  467.           /* since the list is a circular list, the next element is the "oldest" */
  468.           /* element.  If it is full, then all the others are too, so we just wait. */
  469.           /* if all the buffers have been filled, then we'll just spinwait */
  470.           /* here for 0.25 of a second waiting for one to play out.  This could */
  471.           /* be a problem.  If the number of buffers is small, then they might */
  472.           /* appear to be full, but they could still play out in less than 0.25 */
  473.           /* of a second.  See also "may_getchar" where a similar thing is done. */
  474.           WaitForEvent(15);
  475.       }
  476.  
  477.    TryAgainPoint1:
  478.     CurrentBuffer->InUseFlag = 1;
  479.     CurrentBuffer->MySoundCommand.cmd = bufferCmd;
  480.     CurrentBuffer->MySoundCommand.param1 = 0;
  481.     CurrentBuffer->MySoundCommand.param2 = (long)&(CurrentBuffer->Header);
  482.     if (mac_stereo)
  483.       {
  484.           /* stereo sample frames have 2 "samples" in them, so the index */
  485.           /* into the buffer is twice the number of frames. */
  486.         CurrentBuffer->Header.numFrames = buf_index / 2;
  487.       }
  488.      else
  489.       {
  490.         CurrentBuffer->Header.numFrames = buf_index;
  491.       }
  492.     Error = SndDoCommand(MySoundChannel,&(CurrentBuffer->MySoundCommand),1);
  493.     if (queueFull == Error)
  494.       {
  495.           WaitForEvent(2);
  496.         goto TryAgainPoint1;
  497.       }
  498.  
  499.         /* the callback is an interrupt level thing that clears a buffer so */
  500.         /* we can use it again. */
  501.    TryAgainPoint2:
  502.     CurrentBuffer->MyCallbackCommand.cmd = callBackCmd;
  503.     CurrentBuffer->MyCallbackCommand.param2 = (long)&(CurrentBuffer->InUseFlag);
  504.     Error = SndDoCommand(MySoundChannel,&(CurrentBuffer->MyCallbackCommand),1);
  505.     if (queueFull == Error)
  506.       {
  507.           WaitForEvent(2);
  508.         goto TryAgainPoint2;
  509.       }
  510.  
  511.     buf_index = 0;
  512.     CurrentBuffer = CurrentBuffer->Next;
  513.   }
  514.  
  515.  
  516. /* this function is not actually used */
  517. void      output_samples(int left, int right)
  518.   {
  519.       /* in the normal tracker, this is called EVERY TIME a sample frame is */
  520.       /* output.  It normally results in a call to flush_buffer.  Since */
  521.       /* THINK C won't inline functions, this was way to slow, so I dispensed with it */
  522.   }
  523.  
  524.  
  525. /* this flushes the buffer IF another cycle would overflow it.  Since the number */
  526. /* of bytes added to the buffer is SamplingRate / Speed, at high sampling rates */
  527. /* or ridiculously low speeds, larger than the buffer size could be created in */
  528. /* just one call to "resample."  This would be BAD.  Therefore "resample" checks */
  529. /* for this and returns a fatal error if it would happen. */
  530. void      flush_buffer(int BytesGenerated)
  531.   {
  532.     if (buf_index + BytesGenerated >= MAXBUFFERSIZE - 1)
  533.       actually_flush_buffer();
  534.   }
  535.  
  536.  
  537. /* waiting for all samples to play and then closing sound channel & disposing buffers */
  538. void      close_audio(void)
  539.   {
  540.     int                        Scan;
  541.     EventRecord        StupidEvent;
  542.  
  543.     if (buf_index != 0)
  544.         {
  545.             /* make sure that last 1/8 of a second worth of song gets played. */
  546.             actually_flush_buffer();
  547.             }
  548.  
  549.         /* if QuitPending, then we were terminated before the song exited.  Since */
  550.         /* the user probably doesn't want to sit around while all the buffers play */
  551.         /* out, we make the channel shut up. */
  552.         if (QuitPending)
  553.             {
  554.                 SndCommand                Cmd;
  555.                 int                                Loop;
  556.  
  557.                 for (Loop = 0; Loop < ActualNumBuffers; Loop += 1)
  558.                     {
  559.                         /* I don't know why I have to send quietCmd for EVERY pending */
  560.                         /* buffer.  Maybe another sound manager flaw?  */
  561.                         Cmd.cmd = quietCmd;
  562.                         Cmd.param1 = 0;
  563.                         Cmd.param2 = 0;
  564.                         SndDoImmediate(MySoundChannel,&Cmd);
  565.                         Buffers[Loop]->InUseFlag = 0;
  566.                     }
  567.             }
  568.  
  569.     /* wait for all buffers to play out */
  570.     QuitPending = false;
  571.     while (!is_channel_empty() && !QuitPending)
  572.         {
  573.             WaitForEvent(60);
  574.         }
  575.     /* if we receive [a|another] quit event, we just go away */
  576.  
  577.     /* close macintosh sound channel */
  578.     SndDisposeChannel(MySoundChannel,0);
  579.  
  580.     /* release memory allocated for buffers */
  581.     for (Scan = 0; Scan < ActualNumBuffers; Scan += 1)
  582.       {
  583.           /* this isn't really necessary unless you play more than one song */
  584.           /* at a time and the buffer is flushed each time. */
  585.         DisposPtr((void*)Buffers[Scan]);
  586.       }
  587.   }
  588.  
  589.  
  590. /* what is this for? */
  591. void      set_synchro(int s)
  592.   {
  593.   }
  594.  
  595. /* another mystery function */
  596. int       update_frequency(void)
  597.   {
  598.     return 0;
  599.   }
  600.  
  601. void      discard_buffer(void)
  602.   {
  603.       /* throws away buffer.  I tried quietCmd'ing the whole channel, but the */
  604.       /* channel promptly became perfectly useless.  Another sound manager problem? */
  605.       /* Well, you'll just have to put up with long delays as the already queued */
  606.       /* buffers play out. */
  607.     buf_index = 0;
  608.   }
  609.  
  610.  
  611. #if __option(profile)
  612. #define Profiling
  613. #endif
  614.  
  615. #pragma options(!profile)
  616.  
  617. /* asynchronous callback routine which marks buffers as now unused */
  618. /* It would be very bad to profile this since profiling tampers with the */
  619. /* stack invokation and uses A5 global variables!  (believe me, I've tried) */
  620. static pascal void    MyCallBack(SndChannel* Channel, SndCommand* Command)
  621.   {
  622.     *(short*)(Command->param2) = 0;
  623.   }
  624.  
  625. #ifdef Profiling
  626. #pragma options(profile)
  627. #endif
  628.  
  629.  
  630.  
  631.  
  632.  
  633.  
  634.  
  635.  
  636. /* stuff removed from "audio.c" */
  637.  
  638.  
  639. /* prototypes for my single channel resamplers have been added */
  640. void    SampleAntiAliased(struct channel* ch, short* Buffer, short Count);
  641. void    SampleAliased(struct channel* ch, short* Buffer, short Count);
  642.  
  643.  
  644. /* a new "resample" function using my single channel resamplers has been added */
  645. void resample(struct channel *chan, int oversample, int number)
  646.   {
  647.     short      i;
  648.     char*      BufferAddress;
  649.     short*     WA1Shadow;
  650.     short*     WA2Shadow;
  651.     short      BytesGenerated;
  652.  
  653.         /* flush the stuff to the buffer.  i.e. if we would overflow the */
  654.         /* buffer during this cycle, we flush it and get a new buffer. */
  655.         /* later on, checks are performed to make sure we won't overflow the buffer. */
  656.         /* the only time that would happen would be if BytesGenerated is bigger */
  657.         /* than the whole buffer. */
  658.     /* note that flush_buffer now takes a parameter */
  659.     if (mac_stereo)
  660.       {
  661.         BytesGenerated = number * 2;
  662.       }
  663.      else
  664.       {
  665.         BytesGenerated = number;
  666.       }
  667.     flush_buffer(BytesGenerated);
  668.  
  669.     if (BytesGenerated + buf_index > MAXBUFFERSIZE)
  670.       {
  671.         perror("Buffer size not set large enough");
  672.                 FatalError(FatalErrorInternalError);
  673.         end_all();
  674.       }
  675.  
  676.         /* we need a workspace where we construct the channels before scaling */
  677.         /* the final volume.  If we need a bigger one than originally anticipated */
  678.         /* we attempt to reallocate it. */
  679.    Reallocate:
  680.     if (WorkspaceBytes == 0)
  681.       {
  682.         WorkspaceBytes = (number * sizeof(short) + 3) & ~3L;
  683.         WorkspaceArray1 = (void*)NewPtr(WorkspaceBytes);
  684.         WorkspaceArray2 = (void*)NewPtr(WorkspaceBytes);
  685.         if ((WorkspaceArray1 == NULL) || (WorkspaceArray2 == NULL))
  686.           {
  687.             perror("Ran out of memory playing song.");
  688.                         FatalError(FatalErrorOutOfMemory);
  689.             end_all();
  690.           }
  691.       }
  692.      else
  693.       {
  694.         if (WorkspaceBytes < ((number * sizeof(short) + 3) & ~3L))
  695.           {
  696.             DisposPtr((void*)WorkspaceArray1);
  697.             DisposPtr((void*)WorkspaceArray2);
  698.             WorkspaceBytes = 0;
  699.             goto Reallocate;
  700.           }
  701.       }
  702.  
  703.         /* first, erase the workspace, since SampleXXX adds to the workspace. */
  704.     WA1Shadow = WorkspaceArray1;
  705.     WA2Shadow = WorkspaceArray2;
  706.     for (i = WorkspaceBytes / sizeof(long) - 1; i >= 0; i -= 1)
  707.       {
  708.         ((long*)WA1Shadow)[i] = 0;
  709.         ((long*)WA2Shadow)[i] = 0;
  710.       }
  711.         /* oversample == 1 is merely no antialiasing.  I didn't want to fiddle with */
  712.         /* Espie's code, so I just use oversample instead of a new variable "antialiased" */
  713.     if (oversample == 1)
  714.       {
  715.         /* WorkspaceArray1 is the left channel, WorkspaceArray2 is the right channel */
  716.         SampleAliased(&(chan[0]),WA1Shadow,number);
  717.         SampleAliased(&(chan[1]),WA2Shadow,number);
  718.         SampleAliased(&(chan[2]),WA2Shadow,number);
  719.         SampleAliased(&(chan[3]),WA1Shadow,number);
  720.       }
  721.      else
  722.       {
  723.         SampleAntiAliased(&(chan[0]),WA1Shadow,number);
  724.         SampleAntiAliased(&(chan[1]),WA2Shadow,number);
  725.         SampleAntiAliased(&(chan[2]),WA2Shadow,number);
  726.         SampleAntiAliased(&(chan[3]),WA1Shadow,number);
  727.       }
  728.  
  729.     BufferAddress = &(CurrentBuffer->SampleArea[buf_index]);
  730.     if (mac_stereo)
  731.       {
  732.         do
  733.           {
  734.               /* Note how we add both the primary and secondary scaling tables. */
  735.               /* hence each table is independently clipped. */
  736.             /* left channel */
  737.             *(BufferAddress++) = FinalVolumeTablePrimary[(*WA1Shadow) & 0x03ff]
  738.               + FinalVolumeTableSecondary[(*WA2Shadow) & 0x03ff] + 128;
  739.             /* right channel */
  740.             *(BufferAddress++) = FinalVolumeTablePrimary[*(WA2Shadow++) & 0x03ff]
  741.               + FinalVolumeTableSecondary[*(WA1Shadow++) & 0x03ff] + 128;
  742.             number -= 1;
  743.           } while (number > 0);
  744.       }
  745.      else
  746.       {
  747.         do
  748.           {
  749.               /* for mono, we don't have any secondary sound coming in from */
  750.               /* the other channel. */
  751.             *(BufferAddress++) = FinalVolumeTablePrimary[(*(WA1Shadow++)
  752.               + *(WA2Shadow++)) & 0x03ff] + 128;
  753.             number -= 1;
  754.           } while (number > 0);
  755.       }
  756.  
  757.     buf_index += BytesGenerated; /* account for added bytes */
  758.   }
  759.  
  760.  
  761. /* the actual functions for single-channel resampling */
  762.  
  763.  
  764. #ifndef USE_ASSEMBLY_CODE
  765. void      SampleAliased(struct channel* ch, short* Buffer, short Count)
  766.   {
  767.     long        Pointer;
  768.     long        FixLength;
  769.     char*       SampleData;
  770.     long        Step;
  771.     long        LoopLength;
  772.     short*      VolumeMap;
  773.  
  774.     if (ch->mode != DO_NOTHING)
  775.       {
  776.         Pointer = ch->pointer;
  777.         FixLength = ch->samp->fix_length;
  778.         SampleData = ch->samp->start;
  779.         Step = ch->step;
  780.         LoopLength = ch->samp->fix_rp_length;
  781.         VolumeMap = &(SubVolumeTable[(CONSTRAIN(ch->volume,
  782.             MIN_VOLUME,MAX_VOLUME) - MIN_VOLUME) * 256]);
  783.        LoopPoint:
  784.         if (Pointer >= FixLength)
  785.           {
  786.             /* is there a replay ? */
  787.             if (!ch->samp->rp_start)
  788.               {
  789.                 ch->mode = DO_NOTHING;
  790.                 goto OutPoint;
  791.               }
  792.             ch->mode = REPLAY;
  793.             Pointer -= LoopLength;
  794.             goto LoopPoint;
  795.           }
  796.         *(Buffer++) += VolumeMap[(unsigned char)SampleData[fix_to_int(Pointer)]];
  797.         Pointer += Step;
  798.         Count -= 1;
  799.         if (Count > 0)
  800.           {
  801.             goto LoopPoint;
  802.           }
  803.        OutPoint:
  804.         ch->pointer = Pointer;
  805.       }
  806.   }
  807. #else
  808. void      SampleAliased(struct channel* ch, short* Buffer, short Count)
  809.   {
  810.     #define Pointer  D0
  811.     #define Step  D1
  812.     #define FixLength  D2
  813.     #define LocalCount  D3
  814.     #define Temp  D4
  815.     #define Temp2  D5
  816.  
  817.     #define SampleData  A0
  818.     #define VolumeMap  A1
  819.     #define Channel  A2
  820.     #define SampPtr  A3
  821.     #define LocalBuffer  A4
  822.  
  823.     asm
  824.       {
  825.         movem.l      D3-D5/A2-A4,-(A7)
  826.  
  827.         /* loading global variables & parameters:  Must NOT disturb A5 and A6 here */
  828.         move.l      ch,Channel
  829.         move.w      Count,LocalCount
  830.         move.l      OFFSET(struct channel,volume)(Channel),Temp  ;got volume
  831.         cmp.l                #MIN_VOLUME,Temp
  832.                 bge.s                @8
  833.                 moveq.l            #0,Temp  ;if volume < 0, constrain it to 0
  834.         @8: cmp.l                #MAX_VOLUME,Temp
  835.                 bmi.s                @7
  836.                 moveq.l            #MAX_VOLUME,Temp  ;if volume > max volume, constain it to max
  837.     @7:
  838. #if 0 != MIN_VOLUME
  839.                 ;this bit is untested, but I think it will work.  Don't see why
  840.                 ;MIN_VOLUME would ever be != 0, though
  841.                 add.l                #(MIN_VOLUME * 256 * sizeof(short)),Temp  ;table starts at 0, so add this in
  842. #endif
  843.             moveq       #9,Temp2
  844.         lsl.l       Temp2,Temp  ;multiplied by 256*sizeof(short) to obtain offset
  845.         move.l      SubVolumeTable,VolumeMap  ;get base address
  846.         add.l       Temp,VolumeMap  ;add offset
  847.         move.l      Buffer,LocalBuffer
  848.         subq.w      #1,LocalCount
  849.         /* loading sample things */
  850.         move.l      OFFSET(struct channel,samp)(Channel),SampPtr
  851.         move.l      OFFSET(struct sample_info,fix_length)(SampPtr),FixLength
  852.         move.l      OFFSET(struct sample_info,start)(SampPtr),SampleData
  853.         /* loading channel things */
  854.         move.l      OFFSET(struct channel,pointer)(Channel),Pointer
  855.         move.l      OFFSET(struct channel,step)(Channel),Step
  856.         /* clearing high bits of certain registers */
  857.  
  858.         cmp.l       #DO_NOTHING,OFFSET(struct channel,mode)(Channel)
  859.         beq.s       @OutPoint
  860.  
  861.     @3: cmp.l       Pointer,FixLength
  862.         bhi.s       @1
  863.         tst.l       OFFSET(struct sample_info,rp_start)(SampPtr)
  864.         bne.s       @2
  865.         move.l      #DO_NOTHING,OFFSET(struct channel,mode)(Channel)
  866.         bra.s       @OutPoint
  867.     @2: sub.l       OFFSET(struct sample_info,fix_rp_length)(SampPtr),Pointer
  868.         move.l      #REPLAY,OFFSET(struct channel,mode)(Channel)
  869.         bra.s       @3
  870.  
  871.     @1:
  872.         move.l      Pointer,Temp
  873.         moveq       #ACCURACY,Temp2
  874.         lsr.l       Temp2,Temp  ;now Temp is the data index
  875.         moveq.l     #0,Temp2
  876.         move.b      0(SampleData,Temp.L),Temp2  ;get byte
  877. #if __option(mc68020)
  878.         move.w      0(VolumeMap,Temp2.L*2),Temp  ;getting volume corrected value
  879. #else
  880.                 lsl.l                #1,Temp2
  881.         move.w      0(VolumeMap,Temp2.L),Temp
  882. #endif
  883.         add.w       Temp,(LocalBuffer)+  ;adding data into temporary buffer
  884.  
  885.         add.l       Step,Pointer  ;incrementing pointer
  886.  
  887.         dbf         LocalCount,@3
  888.  
  889.     @OutPoint:
  890.         move.l      Pointer,OFFSET(struct channel,pointer)(Channel)
  891.         movem.l     (A7)+,D3-D5/A2-A4
  892.       }
  893.  
  894.     #undef Pointer
  895.     #undef Step
  896.     #undef FixLength
  897.     #undef LocalCount
  898.     #undef Temp
  899.     #undef Temp2
  900.  
  901.     #undef SampleData
  902.     #undef VolumeMap
  903.     #undef Channel
  904.     #undef SampPtr
  905.     #undef LocalBuffer
  906.   }
  907. #endif
  908.  
  909.  
  910. #ifndef USE_ASSEMBLY_CODE
  911. void      SampleAntiAliased(struct channel* ch, short* Buffer, short Count)
  912.   {
  913.     long              Pointer;
  914.     long              FixLength;
  915.     char*             SampleData;
  916.     long              Step;
  917.     long              LoopLength;
  918.     short*            VolumeMap;
  919.     short             Mask;
  920.  
  921.     if (ch->mode != DO_NOTHING)
  922.       {
  923.         Pointer = ch->pointer;
  924.         FixLength = ch->samp->fix_length;
  925.         SampleData = ch->samp->start;
  926.         Step = ch->step;
  927.         LoopLength = ch->samp->fix_rp_length;
  928.         VolumeMap = &(SubVolumeTable[(CONSTRAIN(ch->volume,
  929.             MIN_VOLUME,MAX_VOLUME) - MIN_VOLUME) * 256]);
  930.        LoopPoint:
  931.         if (Pointer >= FixLength)
  932.           {
  933.             /* is there a replay ? */
  934.             if (!ch->samp->rp_start)
  935.               {
  936.                 ch->mode = DO_NOTHING;
  937.                 goto OutPoint;
  938.               }
  939.             ch->mode = REPLAY;
  940.             Pointer -= LoopLength;
  941.             goto LoopPoint;
  942.           }
  943.         /* I don't know if this works.  I use the assembly version since anything */
  944.         /* else is too slow for my computer. */
  945.         Mask = Pointer & (15<<(ACCURACY-4));
  946.         *(Buffer++) += VolumeMap[(unsigned char)
  947.           (AliasTable[((Mask | (unsigned char)SampleData[fix_to_int(Pointer)]) << 1) + 0]
  948.           + AliasTable[((Mask | (unsigned char)SampleData[fix_to_int(Pointer) + 1]) << 1) + 1])];
  949.         Pointer += Step;
  950.         Count -= 1;
  951.         if (Count > 0)
  952.           {
  953.             goto LoopPoint;
  954.           }
  955.        OutPoint:
  956.         ch->pointer = Pointer;
  957.       }
  958.   }
  959. #else
  960. void      SampleAntiAliased(struct channel* ch, short* Buffer, short Count)
  961.   {
  962.     #define Pointer  D0
  963.     #define Mask  D1
  964.     #define Step  D2
  965.     #define FixLength  D3
  966.     #define LocalCount  D4
  967.     #define Temp  D5
  968.     #define Temp2  D6
  969.  
  970.     #define SampleData  A0
  971.     #define AliasMap  A1
  972.     #define VolumeMap  A2
  973.     #define Channel  A3
  974.     #define SampPtr  A4
  975.     #define LocalBuffer  A5
  976.  
  977.     asm
  978.       {
  979.         movem.l      D3-D6/A2-A5,-(A7)
  980.  
  981.         /* loading global variables & parameters:  Must NOT disturb A5 and A6 here */
  982.         move.l      AliasTable,AliasMap
  983.         move.l      ch,Channel
  984.         move.w      Count,LocalCount
  985.         move.l      OFFSET(struct channel,volume)(Channel),Temp  ;got volume
  986.         cmp.l                #MIN_VOLUME,Temp
  987.                 bge.s                @8
  988.                 moveq.l            #0,Temp  ;if volume < 0, constrain it to 0
  989.         @8: cmp.l                #MAX_VOLUME,Temp
  990.                 bmi.s                @7
  991.                 moveq.l            #MAX_VOLUME,Temp  ;if volume > max volume, constain it to max
  992.     @7:
  993. #if 0 != MIN_VOLUME
  994.                 ;this bit is untested, but I think it will work.  Don't see why
  995.                 ;MIN_VOLUME would ever be != 0, though
  996.                 add.l                #(MIN_VOLUME * 256 * sizeof(short)),Temp  ;table starts at 0, so add this in
  997. #endif
  998.             moveq       #9,Temp2
  999.         lsl.l       Temp2,Temp  ;multiplied by 256*sizeof(short) to obtain offset
  1000.         move.l      SubVolumeTable,VolumeMap  ;get base address
  1001.         add.l       Temp,VolumeMap  ;add offset
  1002.         move.l      Buffer,LocalBuffer
  1003.         subq.w      #1,LocalCount
  1004.         /* loading sample things */
  1005.         move.l      OFFSET(struct channel,samp)(Channel),SampPtr
  1006.         move.l      OFFSET(struct sample_info,fix_length)(SampPtr),FixLength
  1007.         move.l      OFFSET(struct sample_info,start)(SampPtr),SampleData
  1008.         /* loading channel things */
  1009.         move.l      OFFSET(struct channel,pointer)(Channel),Pointer
  1010.         move.l      OFFSET(struct channel,step)(Channel),Step
  1011.  
  1012.         /* clearing high bits of certain registers */
  1013. #if __option(mc68020)
  1014.                 ;this is an invariant optimization which is only meaningful on the 68020, so
  1015.                 ;we don't need it if we are compiling for 68000
  1016.         clr.l       Mask
  1017. #endif
  1018.  
  1019.         cmp.l       #DO_NOTHING,OFFSET(struct channel,mode)(Channel)
  1020.         beq.s       @OutPoint
  1021.  
  1022.     @3: cmp.l       Pointer,FixLength
  1023.         bhi.s       @1
  1024.         tst.l       OFFSET(struct sample_info,rp_start)(SampPtr)
  1025.         bne.s       @2
  1026.         move.l      #DO_NOTHING,OFFSET(struct channel,mode)(Channel)
  1027.         bra.s       @OutPoint
  1028.     @2: sub.l       OFFSET(struct sample_info,fix_rp_length)(SampPtr),Pointer
  1029.         move.l      #REPLAY,OFFSET(struct channel,mode)(Channel)
  1030.         bra.s       @3
  1031.  
  1032.     @1:
  1033.         ;calculating anti-aliasing mask
  1034. #if !__option(mc68020)
  1035.                 moveq.l            #0,Mask  ;make sure to clear the high bits each time for 68000
  1036. #endif
  1037.         move.w      Pointer,Mask  ;get lower 16 bits of pointer
  1038.         and.w       #0x0f00,Mask  ;keep only upper 4 bits of fraction
  1039.         ;this conveniently works out to leave 8 bits just below the accuracy
  1040.         ;into which we can stuff bytes. */
  1041. #if ACCURACY != 12
  1042. #error "ACCURACY == 12 is hardwired!"
  1043. #endif
  1044.  
  1045.         ;fetching data bytes
  1046.         move.l      Pointer,Temp
  1047.         moveq       #ACCURACY,Temp2
  1048.         lsr.l       Temp2,Temp  ;now Temp is the data index
  1049.         moveq.l     #0,Temp2
  1050.         move.b      0(SampleData,Temp.L),Mask  ;or in left byte
  1051. #if __option(mc68020)
  1052.         move.b      0(AliasMap,Mask.L*2),Temp2  ;got left product
  1053.         move.b      1(SampleData,Temp.L),Mask  ;or in right byte
  1054.         add.b       1(AliasMap,Mask.L*2),Temp2  ;added in right product
  1055.         move.w      0(VolumeMap,Temp2.L*2),Temp  ;getting volume corrected value
  1056. #else
  1057.         lsl.l                #1,Mask
  1058.         move.b      0(AliasMap,Mask.L),Temp2  ;got left product
  1059.         lsr.l                #1,Mask  ;must shift back, since we OR in a byte next
  1060.         move.b      1(SampleData,Temp.L),Mask  ;or in right byte
  1061.         lsl.l                #1,Mask
  1062.         add.b       1(AliasMap,Mask.L),Temp2  ;added in right product
  1063.         lsl.l                #1,Temp2
  1064.         move.w      0(VolumeMap,Temp2.L),Temp  ;getting volume corrected value
  1065. #endif
  1066.         add.w       Temp,(LocalBuffer)+  ;adding data into temporary buffer
  1067.  
  1068.         add.l       Step,Pointer  ;incrementing pointer
  1069.  
  1070.         dbf         LocalCount,@3
  1071.  
  1072.     @OutPoint:
  1073.         move.l      Pointer,OFFSET(struct channel,pointer)(Channel)
  1074.         movem.l     (A7)+,D3-D6/A2-A5
  1075.       }
  1076.  
  1077.     #undef Pointer
  1078.     #undef Mask
  1079.     #undef Step
  1080.     #undef FixLength
  1081.     #undef LocalCount
  1082.     #undef Temp
  1083.     #undef Temp2
  1084.  
  1085.     #undef SampleData
  1086.     #undef AliasMap
  1087.     #undef VolumeMap
  1088.     #undef Channel
  1089.     #undef SampPtr
  1090.     #undef LocalBuffer
  1091.   }
  1092. #endif
  1093.